const fp = require('lodash/fp');

class Container {

    static of(value) {
        return new Container(value)
    }

    constructor(value) {
        this._value = value;
    }

    map(fn) {
        return Container.of(fn(this._value))
    }
}

class Maybe {
    static of(x) {
        return new Maybe(x)
    }

    isNothing() {
        return this._value === null || this._value === undefined; 
    }

    constructor(x) {
        this._value = x;
    }
    
    map(fn) {
        return this.isNothing() ? this : Maybe.of(fn(this._value));
    }
}

class IO {
    static of (value) {
        return new IO(function() {
            return value;
        })
    }
    constructor(fn) {
        this._value = fn
    }
    map(fn) {
        return new IO(fp.flowRight(fn, this._value))
    }
    join() {
        return this._value();
    }
    flatMap(fn) {
        return this.map(fn).join()
    }

}

// 1.
let maybe = Maybe.of([5, 6, 1]);
let add1 = fp.add(1); 
let ex1 =  maybe.map(fp.map(add1))
console.log(ex1)

// 2
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = xs.map(fp.first)
console.log(ex2)


// 3
let user = {id: 2, name: 'Albert'};
let safeProp = fp.curry((x, o) => Maybe.of(o[x]))
// let ex3 =  (user) => 
//     Maybe.of(user)
//     .map(safeProp('name'))._value
//     .map(fp.first)


// console.log(ex3(user))

let ex31 = IO.of(user)
        .flatMap(safeProp('name'))
        .map(fp.first)

console.log(ex31)





let ex4 = n => {
   return Maybe.of(n)
    .map(parseInt)
}
console.log(ex4(1.23))
